package com.doan.covua;


import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.doan.covua.gamelogic.Move;
import com.doan.covua.gamelogic.MoveGen;
import com.doan.covua.gamelogic.Piece;
import com.doan.covua.gamelogic.Position;
import com.doan.covua.gamelogic.UndoInfo;


public class ChessBoard extends View {
    // Debug
    public static final String   TAG               = "ChessBoard";
    private static final boolean D                 = true;

    protected int                x0, y0, sqSize;

    public boolean               flipped;
    public Position              pos;
    int                          pieceXDelta, pieceYDelta;
    public int                   selectedSquare;
    public int                   selectedOldSquare = -1;
    public boolean               oneTouchMoves;
    public float                 cursorX, cursorY;
    public boolean               cursorVisible;

    private static boolean       isDoMove          = false;
    private static Move          move              = null;

    private ArrayList<Move>      moves             = null;

    private int                  s_BoardSelected   = ChessActivity.s_BoardSelected;
    private int                  s_PieceSelected   = ChessActivity.s_PieceSelected;

    public ChessBoard(Context context, AttributeSet attrs) {
        super(context, attrs);
        pos = new Position();

        x0 = y0 = sqSize = 0;
        pieceXDelta = pieceYDelta = -1;
        flipped = false;
        oneTouchMoves = false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (D) {
            Log.v(TAG, "Vao onDraw");
        }
        boolean animActive = anim.updateState();
        drawBackground(canvas);
        final int width = getWidth();
        final int height = getHeight();
        sqSize = Math.min(getSqSizeW(width), getSqSizeH(height));
        computeOrigin(width, height);
        for (int x = 0; x < 8; x++) {
            for (int y = 0; y < 8; y++) {
                final int xCrd = getXCrd(x);
                final int yCrd = getYCrd(y);
                int sq = Position.getSquare(x, y);
                if (D) {
                    Log.v(TAG, "xCrd =" + xCrd + " yCrd=" + yCrd + "sqSize=" + sqSize + "width=" + width + "height" + height + "sq =" + sq);
                }
                int p = pos.getPiece(sq);
                drawPiece(canvas, xCrd, yCrd, p);
            }
        }

        if ((selectedSquare != -1)) {
            // Trung add: draw select square in first time
            drawResourceSquare(canvas, getX0FromSq(selectedSquare), getY0FromSq(selectedSquare), R.drawable.highlight_sq);

            // Trung add draw legal move of square (piece)
            if (moves != null) {
                for (Move m : moves) {
                    drawResourceSquare(canvas, getX0FromSq(m.to), getY0FromSq(m.to), R.drawable.highlight_legal_move);
                }
                moves = null;
            }
        }

        // Draw two square of last move
        if (isDoMove) {
            if (move != null) {
                drawResourceSquare(canvas, getX0FromSq(move.from), getY0FromSq(move.from), R.drawable.highlight_last_move);
                drawResourceSquare(canvas, getX0FromSq(move.to), getY0FromSq(move.to), R.drawable.highlight_last_move);
                isDoMove = false;
                move = null;
            }
        }
    }

    /*
     * @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int
     * width = getMeasuredWidth(); int height = getMeasuredHeight(); int sqSizeW = getSqSizeW(width); int sqSizeH = getSqSizeH(height); int sqSize =
     * Math.min(sqSizeW, sqSizeH); pieceXDelta = pieceYDelta = -1; // labelBounds = null; if (height > width) { int p = getMaxHeightPercentage();
     * height = Math.min(getHeight(sqSize), height * p / 100); } else { width = Math.min(getWidth(sqSize), width * 65 / 100); }
     * setMeasuredDimension(width, height); }
     */
    public void drawBackground(Canvas canvas) {
        Bitmap mBitmapBackground = null;
        switch (s_BoardSelected) {
        case 1:
            mBitmapBackground = BitmapFactory.decodeResource(getResources(), R.drawable.board);
            break;
        case 2:
            mBitmapBackground = BitmapFactory.decodeResource(getResources(), R.drawable.board_alt);
            break;
        case 3:
            mBitmapBackground = BitmapFactory.decodeResource(getResources(), R.drawable.board_bw);
            break;
        }
        Bitmap bm = Bitmap.createScaledBitmap(mBitmapBackground, getWidth(), getHeight(), true);
        canvas.drawBitmap(bm, 0, 0, null);
    }

    protected int getWidth(int sqSize) {
        return sqSize * 8 + 4;
    }

    protected int getHeight(int sqSize) {
        return sqSize * 8 + 4;
    }

    protected int getSqSizeW(int width) {
        return (width - 4) / 8;
    }

    protected int getSqSizeH(int height) {
        return (height - 4) / 8;
    }

    protected int getMaxHeightPercentage() {
        return 75;
    }

    protected int getXCrd(int x) {
        return x0 + sqSize * (flipped ? 7 - x : x);
    }

    protected int getYCrd(int y) {
        return y0 + sqSize * (flipped ? y : 7 - y);
    }

    protected void computeOrigin(int width, int height) {
        x0 = (width - sqSize * 8) / 2;
        y0 = (height - sqSize * 8) / 2;
    }

    protected int getXFromSq(int sq) {
        return Position.getX(sq);
    }

    protected int getYFromSq(int sq) {
        return Position.getY(sq);
    }

    protected int getXSq(int xCrd) {
        int t = (xCrd - x0) / sqSize;
        return flipped ? 7 - t : t;
    }

    protected int getYSq(int yCrd) {
        int t = (yCrd - y0) / sqSize;
        return flipped ? t : 7 - t;
    }

    protected int getX0FromSq(int sq) {
        int selX = getXFromSq(sq);
        int x0 = getXCrd(selX);
        return x0;
    }

    protected int getY0FromSq(int sq) {
        int selY = getYFromSq(sq);
        int y0 = getYCrd(selY);
        return y0;
    }

    protected final void drawPiece(Canvas canvas, int xCrd, int yCrd, int p) {
        Bitmap mBitmapPiece;
        int pieceId = 0;
        switch (s_PieceSelected) {
        case 1:
            pieceId = Chess2PActivity.pieceImage1[p];
            break;
        case 2:
            pieceId = Chess2PActivity.pieceImage2[p];
            break;
        case 3:
            pieceId = Chess2PActivity.pieceImage3[p];
            break;
        }
        if (D) {
            Log.v(TAG, "pieceId = " + pieceId);
        }
        if (pieceId != 0) {
            mBitmapPiece = BitmapFactory.decodeResource(getResources(), pieceId);
            Bitmap bm = Bitmap.createScaledBitmap(mBitmapPiece, sqSize, sqSize, true);
            canvas.drawBitmap(bm, xCrd, yCrd, null);
        }
    }

    /**
     * Set the board to a given state.
     * 
     * @param pos
     */
    final public void setPosition(Position pos) {
        boolean doInvalidate = false;
        if (!this.pos.equals(pos)) {
            this.pos = new Position(pos);
            doInvalidate = true;
        }
        if (doInvalidate) {
            invalidate();
        }
    }

    /**
     * Set/clear the selected square.
     * 
     * @param square The square to select, or -1 to clear selection.
     */
    final public void setSelection(int square) {
        if (square != selectedSquare) {
            selectedSquare = square;
            invalidate();
        }
    }

    private Handler handlerTimer = new Handler();

    final class AnimInfo {
        AnimInfo() {
            startTime = -1;
        }

        boolean paused;
        long    posHash;  // Position the animation is valid for
        long    startTime; // Time in milliseconds when animation was started
        long    stopTime; // Time in milliseconds when animation should stop
        long    now;      // Current time in milliseconds
        int     piece1, from1, to1, hide1;
        int     piece2, from2, to2, hide2;

        public final boolean updateState() {
            now = System.currentTimeMillis();
            return animActive();
        }

        private final boolean animActive() {
            if (paused || (startTime < 0) || (now >= stopTime)) {
                return false;
            }
            return true;
        }

        public final boolean squareHidden(int sq) {
            if (!animActive()) {
                return false;
            }
            return (sq == hide1) || (sq == hide2);
        }

        public final void draw(Canvas canvas) {
            if (!animActive()) {
                return;
            }
            double animState = (now - startTime) / (double) (stopTime - startTime);
            drawAnimPiece(canvas, piece2, from2, to2, animState);
            // drawPiece(canvas, getXCrd(Position.getX(from1)),
            // getYCrd(Position.getY(from1)), piece2);
            // drawPiece(canvas, getXCrd(to1), getYCrd(to1), piece1);
            // drawPiece(canvas, getXCrd(Position.getX(to1)),
            // getYCrd(Position.getY(to1)), piece1);
            drawAnimPiece(canvas, piece1, from1, to1, animState);
            long now2 = System.currentTimeMillis();
            long delay = 20 - (now2 - now);
            // System.out.printf("delay:%d\n", delay);
            if (delay < 1) {
                delay = 1;
            }
            handlerTimer.postDelayed(new Runnable() {
                public void run() {
                    invalidate();
                }
            }, delay);
            invalidate();
        }

        private void drawAnimPiece(Canvas canvas, int piece, int from, int to, double animState) {
            if (piece == Piece.EMPTY) {
                return;
            }

            final int xCrd1 = getXCrd(Position.getX(from));
            final int yCrd1 = getYCrd(Position.getY(from));
            final int xCrd2 = getXCrd(Position.getX(to));
            final int yCrd2 = getYCrd(Position.getY(to));

            final int xCrd = xCrd1 + (int) Math.round((xCrd2 - xCrd1) * animState);
            final int yCrd = yCrd1 + (int) Math.round((yCrd2 - yCrd1) * animState);

            drawPiece(canvas, xCrd, yCrd, piece);
        }
    }

    AnimInfo anim = new AnimInfo();

    final private boolean myColor(int piece) {
        return (piece != Piece.EMPTY) && (Piece.isWhite(piece) == pos.whiteMove);
    }

    /**
     * Compute the square corresponding to the coordinates of a mouse event.
     * 
     * @param evt Details about the mouse event.
     * @return The square corresponding to the mouse event, or -1 if outside board.
     */
    public int eventToSquare(MotionEvent evt) {
        int xCrd = (int) (evt.getX());
        int yCrd = (int) (evt.getY());

        int sq = -1;
        if (sqSize > 0) {
            int x = getXSq(xCrd);
            int y = getYSq(yCrd);
            if ((x >= 0) && (x < 8) && (y >= 0) && (y < 8)) {
                sq = Position.getSquare(x, y);
            }
        }
        return sq;
    }

    public Move mousePressed(int sq) {
        if (sq < 0) {
            return null;
        }
        if (D) {
            Log.v("selectedSquare = ", "" + selectedSquare);
            Log.v("sq = ", "" + sq);
        }
        // trung add
        if (selectedSquare != -1) {
            int p = pos.getPiece(selectedSquare);
            if (!myColor(p)) {
                setSelection(-1);
            }
        }
        int p = pos.getPiece(sq);
        if (selectedSquare != -1) {
            if (sq != selectedSquare) {
                if (!myColor(p)) {
                    Move m = new Move(selectedSquare, sq, Piece.EMPTY);
                    // setSelection(sq);
                    setSelection(-1);
                    return m;
                } else {
                    setSelection(sq);
                }
            }
            // setSelection(sq);
        } else {
            if (oneTouchMoves) {
                ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(pos);
                moves = MoveGen.removeIllegal(pos, moves);
                Move matchingMove = null;
                int toSq = -1;
                for (Move m : moves) {
                    if ((m.from == sq) || (m.to == sq)) {
                        if (matchingMove == null) {
                            matchingMove = m;
                            toSq = m.to;
                        } else {
                            matchingMove = null;
                            break;
                        }
                    }
                }
                if (matchingMove != null) {
                    setSelection(toSq);
                    return matchingMove;
                }
            }
            if (myColor(p)) {
                setSelection(sq);
            }
        }
        return null;
    }

    /**
     * Set up move animation. The animation will start the next time setPosition is called.
     * 
     * @param sourcePos The source position for the animation.
     * @param move The move leading to the target position.
     * @param forward True if forward direction, false for undo move.
     */
    public void setAnimMove(Position sourcePos, Move move, boolean forward) {
        anim.startTime = -1;
        if (forward) {
            // The animation will be played when pos == targetPos
            Position targetPos = new Position(sourcePos);
            UndoInfo ui = new UndoInfo();
            targetPos.makeMove(move, ui);
        }
        int animTime; // Animation duration in milliseconds.
        {
            int dx = Position.getX(move.to) - Position.getX(move.from);
            int dy = Position.getY(move.to) - Position.getY(move.from);
            double dist = Math.sqrt(dx * dx + dy * dy);
            double t = Math.sqrt(dist) * 100;
            animTime = (int) Math.round(t);
        }
        if (animTime > 0) {
            anim.startTime = System.currentTimeMillis();
            anim.stopTime = anim.startTime + animTime;
            anim.piece2 = Piece.EMPTY;
            anim.from2 = -1;
            anim.to2 = -1;
            anim.hide2 = -1;
            if (forward) {
                int p = sourcePos.getPiece(move.from);
                anim.piece1 = p;
                anim.from1 = move.from;
                anim.to1 = move.to;
                anim.hide1 = anim.to1;
                int p2 = sourcePos.getPiece(move.to);
                if (p2 != Piece.EMPTY) { // capture
                    anim.piece2 = p2;
                    anim.from2 = move.to;
                    anim.to2 = move.to;
                } else if ((p == Piece.WKING) || (p == Piece.BKING)) {
                    boolean wtm = Piece.isWhite(p);
                    if (move.to == move.from + 2) { // O-O
                        anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
                        anim.from2 = move.to + 1;
                        anim.to2 = move.to - 1;
                        anim.hide2 = anim.to2;
                    } else if (move.to == move.from - 2) { // O-O-O
                        anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
                        anim.from2 = move.to - 2;
                        anim.to2 = move.to + 1;
                        anim.hide2 = anim.to2;
                    }
                }
            } else {
                int p = sourcePos.getPiece(move.from);
                anim.piece1 = p;
                if (move.promoteTo != Piece.EMPTY) {
                    anim.piece1 = Piece.isWhite(anim.piece1) ? Piece.WPAWN : Piece.BPAWN;
                }
                anim.from1 = move.to;
                anim.to1 = move.from;
                anim.hide1 = anim.to1;
                if ((p == Piece.WKING) || (p == Piece.BKING)) {
                    boolean wtm = Piece.isWhite(p);
                    if (move.to == move.from + 2) { // O-O
                        anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
                        anim.from2 = move.to - 1;
                        anim.to2 = move.to + 1;
                        anim.hide2 = anim.to2;
                    } else if (move.to == move.from - 2) { // O-O-O
                        anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
                        anim.from2 = move.to + 1;
                        anim.to2 = move.to - 2;
                        anim.hide2 = anim.to2;
                    }
                }
            }
        }
    }

    /**
     * Set/clear the board flipped status.
     * 
     * @param flipped
     */
    public void setFlipped(boolean flipped) {
        if (this.flipped != flipped) {
            this.flipped = flipped;
            invalidate();
        }
    }

    public void setLegalMoveofSquare(ArrayList<Move> moves) {
        this.moves = moves;
    }

    // draw resource for a square
    protected final void drawResourceSquare(Canvas canvas, int xCrd, int yCrd, int resId) {
        if (resId != 0) {
            Bitmap mBitmapPiece = BitmapFactory.decodeResource(getResources(), resId);
            Bitmap bm = Bitmap.createScaledBitmap(mBitmapPiece, sqSize, sqSize, true);
            canvas.drawBitmap(bm, xCrd, yCrd, null);
        }
    }

    // set isDoMove and move to draw square moved
    public static void setLastMove(Move move, boolean isDoMove) {
        ChessBoard.isDoMove = isDoMove;
        ChessBoard.move = move;
    }

    public void doInvalidate() {
        this.invalidate();
    }

    public void setBoardAndPiece(int Board, int Piece) {
        s_BoardSelected = Board;
        s_PieceSelected = Piece;
        invalidate();
    }

}
